package com.gitee.magic.context.converter;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitee.magic.context.ConverterEditorUtils;
import com.gitee.magic.core.annotations.Column;
import com.gitee.magic.core.annotations.Transient;
import com.gitee.magic.core.converter.AbstractConverterEditor;
import com.gitee.magic.core.exception.ApplicationException;
import com.gitee.magic.core.json.JsonNull;
import com.gitee.magic.core.json.JsonObject;
import com.gitee.magic.core.utils.ReflectUtils;
import com.gitee.magic.core.valid.ValidContext;
import com.gitee.magic.core.valid.annotation.NotNull;

/**
 * @author start
 */
public class ObjectConverterEditorExt extends AbstractConverterEditor<String> {

	private static final Logger LOGGER = LoggerFactory.getLogger(ObjectConverterEditorExt.class);

	private Type typeReference;

	public ObjectConverterEditorExt(Class<?> prototype) {
		super(prototype);
	}

	public void setGeneric(Type genericType, Type typeReference) {
		this.typeReference = typeReference;
	}

	@Override
	public void restore(Object value) {
		if (value != null) {
			Class<?> target = value.getClass();
			JsonObject json;
			if (target.equals(JsonObject.class)) {
				json = (JsonObject) value;
			} else {
				json = new JsonObject(String.valueOf(value));
			}
			setSource(json);
			Object instance=ReflectUtils.newInstance(getPrototype());
			Map<Field, PrototypeMethod> fieldMap = getCacheFieldMap(getPrototype());
			for (Field field : fieldMap.keySet()) {
				String fieldName = field.getName();
				PrototypeMethod pm = fieldMap.get(field);
				// 别名
				if (field.isAnnotationPresent(Column.class)) {
					Column alias = field.getAnnotation(Column.class);
					fieldName = alias.value();
				}
				if (LOGGER.isDebugEnabled()) {
					LOGGER.debug("JSON字段名{}，类字段名：{}", fieldName, field.getName());
				}
				Object val = null;
				// 判断是否存在当前KeyValue对
				if (json.has(fieldName)) {
					if (json.isNull(fieldName)) {
						ValidContext.verify(field.getAnnotation(NotNull.class), null, fieldName);
					} else {
						val = json.get(fieldName);
					}
				}
				boolean isContinue = false;
				if (val == null) {
					try {
						// 获取默认值
						val = pm.getReadMethod().invoke(instance);
					} catch (IllegalAccessException e) {
						throw new ApplicationException(e);
					} catch (IllegalArgumentException e) {
						throw new ApplicationException(e);
					} catch (InvocationTargetException e) {
						throw new ApplicationException(e);
					}
					ValidContext.verify(field.getAnnotation(NotNull.class), val, fieldName);
					if (val == null) {
						// 如果默认值也为null则直接跳过
						continue;
					}
					isContinue = true;
				}
				// 校验
				ValidContext.verifyField(field, val);
				if (isContinue) {
					continue;
				}
				AbstractConverterEditor<?> convertObj = ConverterEditorUtils.getFieldConverterEditor(field, null,
						typeReference);
				convertObj.restore(val);
				Object targetValue = convertObj.getValue();
				try {
					pm.getWriteMethod().invoke(instance, targetValue);
				} catch (IllegalAccessException e) {
					throw new ApplicationException(e);
				} catch (IllegalArgumentException e) {
					throw new ApplicationException(e);
				} catch (InvocationTargetException e) {
					throw new ApplicationException(e);
				}
			}
//			Class<?> prototype = getPrototype();
//			do {
//				Field[] fields = prototype.getDeclaredFields();
//				for (Field field : fields) {
//					if (Modifier.isFinal(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) {
//						continue;
//					}
//					if (field.isAnnotationPresent(Transient.class)) {
//						continue;
//					}
//					String fieldName = field.getName();
//					PropertyDescriptor pd = null;
//					try {
//						pd = new PropertyDescriptor(fieldName, prototype);
//					} catch (IntrospectionException e) {
//						if (LOGGER.isErrorEnabled()) {
//							LOGGER.error("Class[{}],Type:[{}],Field:[{}],未获取到get,set方法", prototype, field.getType(),
//									fieldName);
//						}
//						throw new ApplicationException(e);
//					}
//					// 别名
//					if (field.isAnnotationPresent(Column.class)) {
//						Column alias = field.getAnnotation(Column.class);
//						fieldName = alias.value();
//					}
//					if (LOGGER.isDebugEnabled()) {
//						LOGGER.debug("JSON字段名{}，类字段名：{}", fieldName, field.getName());
//					}
//					Object val = null;
//					// 判断是否存在当前KeyValue对
//					if (json.has(fieldName)) {
//						if (json.isNull(fieldName)) {
//							ValidContext.verify(field.getAnnotation(NotNull.class), null,fieldName);
//						} else {
//							val = json.get(fieldName);
//						}
//					}
//					boolean isContinue = false;
//					if (val == null) {
//						try {
//							// 获取默认值
//							val = pd.getReadMethod().invoke(instance);
//						} catch (IllegalAccessException e) {
//							throw new ApplicationException(e);
//						} catch (IllegalArgumentException e) {
//							throw new ApplicationException(e);
//						} catch (InvocationTargetException e) {
//							throw new ApplicationException(e);
//						}
//						ValidContext.verify(field.getAnnotation(NotNull.class), val,fieldName);
//						if (val == null) {
//							// 如果默认值也为null则直接跳过
//							continue;
//						}
//						isContinue = true;
//					}
//					// 校验
//					ValidContext.verifyField(field, val);
//					if (isContinue) {
//						continue;
//					}
//					AbstractConverterEditor<?> convertObj = ConverterEditorUtils.getFieldConverterEditor(field,null,typeReference);
//					convertObj.restore(val);
//					Object targetValue = convertObj.getValue();
//					try {
//						pd.getWriteMethod().invoke(instance, targetValue);
//					} catch (IllegalAccessException e) {
//						throw new ApplicationException(e);
//					} catch (IllegalArgumentException e) {
//						throw new ApplicationException(e);
//					} catch (InvocationTargetException e) {
//						throw new ApplicationException(e);
//					}
//				}
//			} while (!(prototype = prototype.getSuperclass()).equals(Object.class));
			setValue(instance);
		}
	}

	@Override
	public String converter() {
		if (getSource() == null) {
			return null;
		}
		return getSource().toString();
	}

	@Override
	public void setValue(Object v) {
		super.setValue(v);
		if (getValue() != null) {
			JsonObject json = new JsonObject();
			Map<Field, PrototypeMethod> fieldMap = getCacheFieldMap(getValue().getClass());
			for (Field field : fieldMap.keySet()) {
				String fieldName = field.getName();
				PrototypeMethod pm = fieldMap.get(field);
				// 别名
				if (field.isAnnotationPresent(Column.class)) {
					Column alias = field.getAnnotation(Column.class);
					fieldName = alias.value();
				}
				Object value = null;
				try {
					// 获取默认值
					value = pm.getReadMethod().invoke(getValue());
				} catch (IllegalAccessException e) {
					throw new ApplicationException(e);
				} catch (IllegalArgumentException e) {
					throw new ApplicationException(e);
				} catch (InvocationTargetException e) {
					throw new ApplicationException(e);
				}
				// NULL直接跳过
				if (value == null) {
					if (field.isAnnotationPresent(JsonNull.class)) {
						json.put(fieldName, JsonObject.NULL);
					}
					continue;
				}
				AbstractConverterEditor<?> convertEditor = ConverterEditorUtils.getFieldConverterEditor(field,
						value.getClass(), null);
				convertEditor.setValue(value);
				json.put(fieldName, convertEditor.getSource());
			}

//			Class<?> prototype=getValue().getClass();
//			do {
//				Field[] fields=prototype.getDeclaredFields();
//				for(Field field:fields) {
//					if(Modifier.isFinal(field.getModifiers())||Modifier.isStatic(field.getModifiers())) {
//						continue;
//					}
//					if (field.isAnnotationPresent(Transient.class)) {
//						continue;
//					}
//					String fieldName=field.getName();
//					PropertyDescriptor pd=null;
//					try {
//						pd = new PropertyDescriptor(fieldName, prototype);
//					} catch (IntrospectionException e) {
//						if(LOGGER.isErrorEnabled()) {
//							LOGGER.error("Class[{}],Type:[{}],Field:[{}],未获取到get,set方法",prototype,field.getType(),fieldName);
//						}
//						throw new ApplicationException(e);
//					}
//					//别名
//					if(field.isAnnotationPresent(Column.class)) {
//						Column alias=field.getAnnotation(Column.class);
//						fieldName=alias.value();
//					}
//					Object value=null;
//					try {
//						//获取默认值
//						value=pd.getReadMethod().invoke(getValue());
//					} catch (IllegalAccessException e) {
//						throw new ApplicationException(e);
//					} catch (IllegalArgumentException e) {
//						throw new ApplicationException(e);
//					} catch (InvocationTargetException e) {
//						throw new ApplicationException(e);
//					}
//					//NULL直接跳过
//					if(value==null) {
//						continue;
//					}
//					AbstractConverterEditor<?> convertEditor =ConverterEditorUtils.getFieldConverterEditor(field,value.getClass(),null);
//					convertEditor.setValue(value);
//					json.put(fieldName, convertEditor.getSource());
//				}
//			}while(!(prototype=prototype.getSuperclass()).equals(Object.class));
			setSource(json);
		}
	}

	public static Map<String, Map<Field, PrototypeMethod>> FIELD_CACHE_MAP = new HashMap<>(200);

	public static Map<Field, PrototypeMethod> getCacheFieldMap(Class<?> prop) {
		if (FIELD_CACHE_MAP.containsKey(prop.getName())) {
			return FIELD_CACHE_MAP.get(prop.getName());
		}
		Map<Field, PrototypeMethod> fieldMap = getFieldMap(prop);
		FIELD_CACHE_MAP.put(prop.getName(), fieldMap);
		return fieldMap;
	}
	
	public static Map<Field, PrototypeMethod> getFieldMap(Class<?> prop) {
		Map<Field, PrototypeMethod> fieldMap = new HashMap<>(100);
		Class<?> prototype = prop;
		do {
			Field[] fields = prototype.getDeclaredFields();
			for (Field field : fields) {
				if (Modifier.isFinal(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) {
					continue;
				}
				if (field.isAnnotationPresent(Transient.class)) {
					continue;
				}
				String fieldName = field.getName();
				PropertyDescriptor pd = null;
				try {
					pd = new PropertyDescriptor(fieldName, prototype);
				} catch (IntrospectionException e) {
					if (LOGGER.isErrorEnabled()) {
						LOGGER.error("Class[{}],Type:[{}],Field:[{}],未获取到get,set方法", prototype, field.getType(),
								fieldName);
					}
					throw new ApplicationException(e);
				}
				PrototypeMethod pm=new PrototypeMethod();
				try {
					pm.setReadMethod(pd.getReadMethod());
				} catch (IllegalArgumentException e) {
					throw new ApplicationException(e);
				} 
				try {
					pm.setWriteMethod(pd.getWriteMethod());
				} catch (IllegalArgumentException e) {
					throw new ApplicationException(e);
				}
				fieldMap.put(field, pm);
			}
		} while (!(prototype = prototype.getSuperclass()).equals(Object.class));
		return fieldMap;
	}

	static class PrototypeMethod {
		
		private Method writeMethod;
		private Method readMethod;

		public Method getWriteMethod() {
			return writeMethod;
		}

		public void setWriteMethod(Method writeMethod) {
			this.writeMethod = writeMethod;
		}

		public Method getReadMethod() {
			return readMethod;
		}

		public void setReadMethod(Method readMethod) {
			this.readMethod = readMethod;
		}

	}

}
